#include <iostream>     // std::cout, std::cin
#include <algorithm>    // std::sort
#include <vector>       // std::vector
#include <set>       // std::vector

#include "Ej2.h"

using namespace std;

//~ Edificio::Edificio(int _izq, int _alt, int _der): izq(_izq), alt(_alt), der(_der){}

Flanco::Flanco(unsigned int _x,unsigned int _alt,bool _subida,unsigned int _idEdificio): x(_x), alt(_alt), subida(_subida), idEdificio(_idEdificio){}
Edificio::Edificio(unsigned int _alt,unsigned int _idEdificio):alt(_alt), idEdificio(_idEdificio){}
SolucionParcial::SolucionParcial(unsigned int _x,unsigned int _y):x(_x), y(_y){}



//~ vector<Edificio> recibir_entradas()
//~ {
  //~ int n, _izq, _alt, _der;
  //~ cin >> n;			//cantidad de edificios
  //~ vector<Edificio> edificios;
  //~ 
  //~ for(int i = 0; i < n; i++)
  //~ {
	 //~ cin >> _izq;
	 //~ cin >> _alt;
	 //~ cin >> _der;;
    //~ Entrada x = Edificio(_izq, _alt, _der);
    //~ 
    //~ edificios.push_back(x);
  //~ }  
//~ 
 //~ return edificios;
//~ }


vector<Flanco> recibir_entradas()
{
  unsigned int n, _izq, _alt, _der;
  cin >> n;			//cantidad de edificios  
  vector<Flanco> flancos;
  
  for(unsigned int i = 0; i < n; i++)
  {
	 cin >> _izq;
	 cin >> _alt;
	 cin >> _der;
	 flancos.push_back(Flanco(_izq,_alt,true,i));
	 flancos.push_back(Flanco(_der,_alt,false,i));    
  }  

 return flancos;
}

//~ bool comparacion (Edificio i, Edificio j)
//~ {
//~ return (i.izq < j.izq);
//~ }

bool comparacionF (Flanco i, Flanco j)
{
return (i.x < j.x);
}

class Comparador
{
public:
    bool operator()(const Edificio& e1, const Edificio& e2)
    {
		if(e1.alt != e2.alt)
			return e1.alt < e2.alt;
		else
			return e1.idEdificio < e2.idEdificio;
    }
    
};


void ej2()
{
	//Vector de flancos	
	vector<Flanco> flancos = recibir_entradas();
	//ordeno los flancos nlog(n)
	sort(flancos.begin(), flancos.end(), comparacionF);
	
	pair<std::set<Edificio>::iterator,bool> ret;
	
	vector<Flanco>::iterator iterFlancos = flancos.begin();
	//Edificio que se está "dibujando" actualmente
	Edificio edificioActual(iterFlancos->alt,iterFlancos->idEdificio);
	//Solucion candidata, se usa para poder comparar con edificioActual y evitar problemas de solapamiento, la solución se imprimirá cdo se sepa que no está solapada
	SolucionParcial solCandidata(iterFlancos->x,iterFlancos->alt);	
	iterFlancos++;	
	
	//Arreglo que contendrá las posiciones de los edificios agregados en alturas para que la eliminacion
	//del conjunto cueste O(1)
	set<Edificio>::iterator edificios[flancos.size()/2+1];
	
	set<Edificio,Comparador> alturas;
	//Agrego la base del dibujo (0)
	ret = alturas.insert(Edificio(0,flancos.size()/2));
	edificios[flancos.size()/2] = ret.first;	
	
	set<Edificio>::iterator tempIter;
	
	for (vector<Flanco>::iterator flanco = iterFlancos; flanco != flancos.end(); flanco++){	
		//~ Si el flanco que encontre corresponde al edificio actual entonces es un flanco de bajada
		//~ Caso 4		
		if(flanco->idEdificio == edificioActual.idEdificio){
			tempIter = alturas.end();
			tempIter--;			
			//~ No se agrega a la solución a menos que la altura sea diferente
			if(edificioActual.alt != tempIter->alt){			
				cout <<solCandidata.x <<" "<<solCandidata.y << " ";				
				solCandidata.x = flanco->x;	solCandidata.y = tempIter->alt;
			}				
			edificioActual = *tempIter;
			alturas.erase(tempIter);
		//El flanco encontrado es de subida, por ende, corresponde a otro edificio
		}else if(flanco->subida){
			//si la altura es mayor a la actual
			//Se agrega a la solucion
			//Caso 1
			if(flanco->alt > edificioActual.alt){
				//Para evitar problema de edificios solapados por flanco de subida
				if(flanco->x != solCandidata.x){
					cout <<solCandidata.x <<" "<<solCandidata.y << " ";
				}	
				solCandidata.x = flanco->x;	solCandidata.y = flanco->alt;
							
				ret  = alturas.insert(edificioActual);
				edificios[edificioActual.idEdificio] = ret.first;
								
				edificioActual.alt = flanco->alt;
				edificioActual.idEdificio = flanco->idEdificio;
				
			//~ Flanco de subida con altura menor o igual a la del edificio actual
			//~ Caso 5
			}else{
				ret = alturas.insert(Edificio(flanco->alt,flanco->idEdificio));
				edificios[flanco->idEdificio] = ret.first;
			}		
		//Si el flanco encontrado es de bajada y no es del edificio actual
		//Caso 2
		}else{
			alturas.erase(edificios[flanco->idEdificio]);
		}		
	}	
	cout <<solCandidata.x <<" "<<solCandidata.y << endl;
}


int main()
{
  while(cin.peek() != 48) {
    ej2();
    cin.get();
  }
  
  return 0;
}
